//
//  PJMTool.swift
//  PJMProFramework
//
//  Created by hui wang on 2024/11/24.
//

import UIKit
import Foundation

class PJMTool: NSObject {
    
}

public enum PJMUITool {
    
    public static var rootWindow: UIWindow? {
        if let rootWindow = windowScene?.windows.last(where: { $0 is UIWindow}) {
            return rootWindow
        }
        return windowScene?.windows.first
    }
    
    public static var keyWindow: UIWindow? {
        return windowScene?.windows.first(where: { $0 is UIWindow })
    }
    
    public static var viewController: UIViewController {
        let root = PJMUITool.rootWindow?.rootViewController
        let vc = getCurrentVC(checkVC: root)
        return vc
    }
    
    private static func getCurrentVC(checkVC: UIViewController?) -> UIViewController {
        let currentVC: UIViewController
        if let navVC = checkVC as? UINavigationController {
            currentVC = self.getCurrentVC(checkVC: navVC.visibleViewController ?? navVC.viewControllers.last)
        }  else {
            currentVC = checkVC ?? PJMUITool.rootWindow?.rootViewController ?? UIViewController()
        }
        return currentVC.presentedViewController ?? currentVC
    }
    
    public static var windowScene: UIWindowScene? {
        let connectedScenes = UIApplication.shared.connectedScenes
        if let windowScene = connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
            return windowScene
        }
        return connectedScenes.first as? UIWindowScene
    }
    
    public static var statusBarFrame: CGRect {
        return windowScene?.statusBarManager?.statusBarFrame ?? .zero
    }
    
    static func statusBarHeight() -> CGFloat {
        statusBarFrame.height
    }
    
    public static var screenWidth: CGFloat {
        return keyWindow?.bounds.width ?? 0
    }
    
    public static var screenHeight: CGFloat {
        return keyWindow?.bounds.height ?? 0
    }
    
    public static var screenSize: CGSize {
        return keyWindow?.bounds.size ?? .zero
    }
    
    public static func safeTop() -> CGFloat {
        guard let windowScene = windowScene else {return 0}
        guard let window = windowScene.windows.first else {return 0}
        return window.safeAreaInsets.top > 0 ? window.safeAreaInsets.top : window.rootViewController?.tabBarController?.tabBar.safeAreaInsets.top ?? 0
    }
    
    public static func safeBottom() -> CGFloat {
        guard let windowScene = windowScene else {return 0}
        guard let window = windowScene.windows.first else {return 0}
        return window.safeAreaInsets.bottom
    }
    
    public static var contactWidth: CGFloat = 525
    
    
    public static func generateRandomString(length: Int) -> String {
        let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        return (0..<length).compactMap { _ in
            letters.randomElement()?.description
        }.joined()
    }
    
    public static func getSystemLanguageUsingNSLocale() -> String {
        let preferredLanguages = NSLocale.preferredLanguages
        if let firstLanguage = preferredLanguages.first {
            return firstLanguage
        }
        return ""
    }
    
    public static  func setAppLanguage(to language: String) {
        let preferredLanguages = UserDefaults.standard.object(forKey: "AppleLanguages") as? [String] ?? []
        if !preferredLanguages.contains(language) {
            UserDefaults.standard.set([language] + preferredLanguages, forKey: "AppleLanguages")
            UserDefaults.standard.synchronize()
        }
    }
    
}

struct AnyCodingKey: CodingKey {
    var stringValue: String
    var intValue: Int?
    
    init?(stringValue: String) {
        self.stringValue = stringValue
    }
    
    init?(intValue: Int) {
        self.intValue = intValue
        self.stringValue = String(intValue)
    }
    
    init<Key: CodingKey>(_ base: Key) {
        if let intValue = base.intValue {
            self.init(intValue: intValue)!
        } else {
            self.init(stringValue: base.stringValue)!
        }
    }
}

public struct AnyCodable: Decodable {
    public private(set) var value: Any
    public init<T>(_ value: T?) {
        self.value = value ?? ()
    }
    
    public init(from decoder: Decoder) throws {
        if let container = try? decoder.container(keyedBy: AnyCodingKey.self) {
            var result = [String: Any]()
            try container.allKeys.forEach { (key) throws in
                result[key.stringValue] = try container.decode(AnyCodable.self, forKey: key).value
            }
            value = result
        } else if var container = try? decoder.unkeyedContainer() {
            var result = [Any]()
            while !container.isAtEnd {
                result.append(try container.decode(AnyCodable.self).value)
            }
            value = result
        } else if let container = try? decoder.singleValueContainer() {
            if let intVal = try? container.decode(Int.self) {
                value = intVal
            } else if let int8 = try? container.decode(Int8.self) {
                value = int8
            } else if let int16 = try? container.decode(Int16.self) {
                value = int16
            } else if let int32 = try? container.decode(Int32.self) {
                value = int32
            } else if let int64 = try? container.decode(Int64.self) {
                value = int64
            } else if let uint = try? container.decode(UInt.self) {
                value = uint
            } else if let uint8 = try? container.decode(UInt8.self) {
                value = uint8
            } else if let uint16 = try? container.decode(UInt16.self) {
                value = uint16
            } else if let uint32 = try? container.decode(UInt32.self) {
                value = uint32
            } else if let uint64 = try? container.decode(UInt64.self) {
                value = uint64
            } else if let floatVal = try? container.decode(Float.self) {
                value = floatVal
            } else if let doubleVal = try? container.decode(Double.self) {
                value = doubleVal
            } else if let boolVal = try? container.decode(Bool.self) {
                value = boolVal
            } else if let stringVal = try? container.decode(String.self) {
                value = stringVal
            } else if container.decodeNil() {
                value = NSNull()
            } else {
                throw DecodingError.dataCorruptedError(in: container, debugDescription: "the container contains nothing serialisable")
            }
        } else {
            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not serialise"))
        }
    }
}


public extension Encodable {
    var toDict: [String: Any]? {
        do {
            return try JSONSerialization.jsonObject(with: self.toData ?? Data(), options: []) as? [String : Any]
        } catch {
            return nil
        }
    }
    var toData: Data? {
        if let data = try? JSONEncoder().encode(self) {
            return data
        }
        return nil
    }
}
